/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
/**
 * \file
 * \brief PNG encoder interface using the libpng library.
 * \author Antti Maula <antti.maula@tkk.fi>
 */
#ifndef _PNGENCODER_HPP_
#define _PNGENCODER_HPP_
#include <stdio.h>
#include <png.h>
#include <setjmp.h>

/**
 * PNG encoder interface using the libpng.
 *
 * This is CPNGEncoder version 1.0.
 * 
 *
 *
 * Version 1.1 (26.9.2011)
 * - Added parameter facilities for bit depth, color type and bytesperpixel.
 *
 * Version 1.0 (9.7.2007)
 * - Initial implementation of the PNGEncoder interface. Provides
 *   basic facilities to .. surprisingly enough .. Encode PNG images.
 *
 */
class CPNGEncoder 
{
public:

  /**
   * Default constructor. If called with parameters, the assigned
   * pointer is assigned as PNG source. See 'SetPNGDataPtr' for more
   * info.
   * 
   * @param[in] aData           Pointer to PNG data to encode.
   * @param[in] aDataLen        Maximum available datasize in this buffer.
   */
  CPNGEncoder(void);


  /**
   * Default destructor.
   */
  ~CPNGEncoder();
  

  /** Assign a pointer to RGB buffer (8 bit depth, 3 bytes per pixel).
   *
   * This function automatically sets encoding parameters to match the
   * appropriate type - in fact - this function is just an convenience
   * wrapper for the SetRawDataPtr() function, setting parameters:
   * (PNG_COLOR_TYPE_RGB, 8, 3)
   *
   * Assigns a pointer to read data from. NOTE: This only stores the
   * datasource pointer, if the COPY is not set (which of course
   * should be avoided to save processing power)
   *
   * @param[in] aData           Pointer to data to encode.
   * @param[in] aDataLen        Maximum available datasize in this buffer.
   * @param[in] aCopy           'true' - Duplicate provided data; 'false' 
   *                            - point to original data region (region must stay
   *                            intact until encoding is complete)
   * @return                    true == success, false == failure. Note that
   *                            no analysis is done on the data itself
   *                            during this call.
   *
   */
  bool SetRGBDataPtr(const char *aData, const int aDataLen, 
                     const unsigned int aWidth, const unsigned int aHeight, 
                     bool aCopy = false);


  /** Assign a pointer to raw memory area to encode, using the given
   * format parameters for encoding.
   *
   * This function automatically sets encoding parameters to match the
   * appropriate type.
   *
   * Assigns a pointer to read data from. NOTE: This only stores the
   * datasource pointer, if the COPY is not set (which of course
   * should be avoided to save processing power)

   *
   * @param[in] aData           Pointer to data to encode.
   * @param[in] aDataLen        Maximum available datasize in this buffer.
   * @param[in] aWidth          Image width
   * @param[in] aHeight         Image height
   * @param[in] aColorType      Color type, must be one of PNG_COLOR_TYPE_* constants.
   * @param[in] aBitDepth       Bit depth. Must match colortype, see libpng documentation. Usually 8. :)
   * @param[in] aBytesPerPixel  How many bytes stored per pixel? 3 for RGB for example.
   * @param[in] aCopy           'true' - Duplicate provided data; 'false', and point to original 
   *                            data region (region must stay intact until encoding is complete)
   * @return                    true == success, false == failure. Note that
   *                            no analysis is done on the data itself
   *                            during this call.
   *
   */ 
  bool SetRawDataPtr(const char *aData, const int aDataLen, 
                     const unsigned int aWidth, 
                     const unsigned int aHeight, 
                     const unsigned int aColorType, 
                     const unsigned int aBitDepth,
                     const unsigned int aBytesPerPixel,
                     bool aCopy); 
                                

  /** This function requests a Encode for the whole image data.
   *
   * Calling this function encodes the currently assigned PNG buffer
   * to internal RGB buffer. After this call has returned, possible
   * external PNG datapointers can be released
   *
   * @return                    'true' if encoding was succesfull, and data
   *                            is now fetchable by GetRawDataPtr or
   *                            GetRawDataCopy functions.
   */
  bool Encode(void);


  /**
   * This function returns a pointer to beginning of RGB data. (In format
   * <red><green><blue><red><green>...) All single color components are 8 bits.
   * The pointer points to internal buffer, so modifying the returned buffer
   * is not allowed. Also, while using the data the instance must be kept
   * alive. Also, calling the SetPNGDataPtr invalidates the pointer.
   * 
   * @return                    Pointer to RGB data or NULL on failure.
   *
   */
  const char *GetPNGDataPtr(void);


  /**
   * Works just like \see GetRawDataPtr, except that the data is copied to
   * separate user-supplied buffer. This avoids buffer invalidation in case of
   * changing ptr.
   * 
   * @param[in] aDataPtr        Pointer to memory location to copy the RGB data.
   * @param[in] aMaxBufSize     Buffer size available. (May not be used completely,
   *                            but atleast the number of bytes returned by
   *                            GetRawDataSize(void) is required)
   * @return                    true == success. Image Encompress was succesfull,
   *                            and the data is now stored to user supplied pointer.
   *                            false == failure. Image encompression failed, or 
   *                            user supplied buffer was too small.
   *
   */ 
  bool GetPNGDataCopy(char *aDataPtr, const unsigned int aMaxBufSize);


  /**
   * Returns the number of bytes required by RGB data. 
   * 
   * @return                    Positive number of bytes if PNG Header was 
   *                            valid, otherwise -1 on error.
   *  
   */
  int GetPNGDataSize(void);

  
  /**
   * Returns the image width in pixels. 
   *
   * @return                    Positive number of pixels if PNG header was
   *                            valid, otherwise -1 on error.
   */
  int GetImageWidth(void);


  /**
   * Returns the image height in pixels. 
   *
   * @return                    Positive number of pixels if PNG header was
   *                            valid, otherwise -1 on error.
   */
  int GetImageHeight(void);




private:
  bool InitEncode(void);
  void ReservePNGData(const unsigned int aSizeBytes);
  void ReserveRawData(const unsigned int aSizeBytes);
  bool SetEncodingParameters(const unsigned int aColorType,
                             const unsigned int aBitDepth,
                             const unsigned int aBPP);
private:
  // Encoded PNG data pointer here.
  char *iPNGData;
  unsigned int iPNGDataSize;
  unsigned int iPNGDataReserved;

  // Decoded RGB data here.
  char *iRawData;
  unsigned int iRawDataSize;
  unsigned int iRawDataReserved;
  bool iRawDataOwned;

  // PNG Stuff.
  png_structp iPNGEncoderPtr;
  png_infop iPNGInfoPtr;

  // Status flags
  bool iGotPNG;

  // Encoded information
  unsigned int iWidth;
  unsigned int iHeight;
  int iColorType;
  int iBitDepth;
  int iCompressionLevel;
  int iBytesPerPixel;

private:
  static void WriteDataToOutputStream(png_structp png_ptr, 
                                      png_bytep outBytes,
                                      png_size_t byteCountToRead);
  static void FlushDataToOutputStream(png_structp png_ptr);
  unsigned int iWriteDataIndex;
  
private:

  // Private constructors (Copying of PNGEncoder object denied)
  CPNGEncoder(const CPNGEncoder &)
    : iPNGData(),
      iPNGDataSize(),
      iPNGDataReserved(),
      iRawData(),
      iRawDataSize(),
      iRawDataReserved(),
      iRawDataOwned(),
      iPNGEncoderPtr(),
      iPNGInfoPtr(),
      iGotPNG(),
      iWidth(),
      iHeight(),
      iColorType(),
      iBitDepth(),
      iCompressionLevel(),
      iBytesPerPixel(0),
      iWriteDataIndex() {}
  CPNGEncoder &operator =(const CPNGEncoder &) { return *this; }
};

/*****************************************************************************\
 *
 * $Log: not supported by cvs2svn $
 * Revision 1.2  2009-09-29 12:07:35  amaula
 * Some stuff, nothing surely works.
 *
 * Revision 1.1  2009-09-29 11:50:26  amaula
 * Definitely non-working base code. Just a renamed duplicate of decoder part.
 *
\*****************************************************************************/

#endif
